import numpy as np
import pandas as pd
import os,sys
from pathlib import Path
import matplotlib.pyplot as plt
import plotly.express as px
import PV_ICE
cwd = os.getcwd() #grabs current working directory
testfolder = str(Path().resolve().parent.parent / 'PV_ICE' / 'TEMP' / 'MESC-NRELStdScens')
inputfolder = str(Path().resolve().parent.parent / 'PV_ICE' / 'baselines'/'NRELStdScenarios')
baselinesfolder = str(Path().resolve().parent.parent /'PV_ICE' / 'baselines')
supportMatfolder = str(Path().resolve().parent.parent / 'PV_ICE' / 'baselines' / 'SupportingMaterial')
#altBaselinesfolder = str(Path().resolve().parent.parent / 'PV_ICE' / 'baselines' / 'Energy_CellModuleTechCompare')
if not os.path.exists(testfolder):
os.makedirs(testfolder)
print("Python version ", sys.version)
print("Pandas version ", pd.__version__)
print("pyplot ", plt.matplotlib.__version__)
print("PV_ICE version ", PV_ICE.__version__)
Python version 3.11.5 | packaged by Anaconda, Inc. | (main, Sep 11 2023, 13:26:23) [MSC v.1916 64 bit (AMD64)] Pandas version 2.0.3 pyplot 3.7.2 PV_ICE version
https://scenarioviewer.nrel.gov/ download the data, save it to the NRELStdScenarios folder in baselines.
ReEDS capacity data is cumulative, and only reports even years, so we need to data munge.
stdsceninput_raw = pd.read_csv(os.path.join(inputfolder, 'StdScen23_Mid_Case_annual_national.csv'),
skiprows=[0,1,2,4], header=[0], index_col=1)
#other scenario options:
#StdScen23_Mid_Case_NoNascent_annual_national.csv
stdscen_pv_evens = stdsceninput_raw.filter(like='(MW)').filter(like='PV')
#take the difference betwen even years to get annual additions from cumulative
stdscens_evens_added_cap = stdscen_pv_evens.diff()
#divide by 2 to evenly distribute across odd and even years
stdscens_added_cap = stdscens_evens_added_cap/2
#now make previous odds = next year evens deployment
idx_temp = pd.RangeIndex(start=2024,stop=2051,step=1) #create the index
stdscens_added_cap_filled = stdscens_added_cap.reindex(idx_temp, method='bfill')
stdscens_added_cap_filled
| Behind-the-meter PV capacity (MW) | Utility-scale PV capacity (MW) | |
|---|---|---|
| 2024 | NaN | NaN |
| 2025 | 6535.55 | 20784.15 |
| 2026 | 6535.55 | 20784.15 |
| 2027 | 8883.10 | 23685.35 |
| 2028 | 8883.10 | 23685.35 |
| 2029 | 12385.30 | 9459.10 |
| 2030 | 12385.30 | 9459.10 |
| 2031 | 9558.50 | 19190.80 |
| 2032 | 9558.50 | 19190.80 |
| 2033 | 6421.50 | 42129.65 |
| 2034 | 6421.50 | 42129.65 |
| 2035 | 2684.05 | 53625.70 |
| 2036 | 2684.05 | 53625.70 |
| 2037 | 2488.95 | 29024.65 |
| 2038 | 2488.95 | 29024.65 |
| 2039 | 2407.60 | 32607.90 |
| 2040 | 2407.60 | 32607.90 |
| 2041 | 2478.40 | 36283.55 |
| 2042 | 2478.40 | 36283.55 |
| 2043 | 2807.05 | 25049.15 |
| 2044 | 2807.05 | 25049.15 |
| 2045 | 2781.95 | 34141.90 |
| 2046 | 2781.95 | 34141.90 |
| 2047 | 3182.55 | 30891.35 |
| 2048 | 3182.55 | 30891.35 |
| 2049 | 3566.90 | 29768.90 |
| 2050 | 3566.90 | 29768.90 |
#Reeds is in MWac, we're in MWdc, so multiply residential by 1.1 and utility by 1.3
stdscens_pv_filled_dc = pd.DataFrame()
stdscens_pv_filled_dc['Behind-the-meter PV capacity (MWdc)'] = stdscens_added_cap_filled['Behind-the-meter PV capacity (MW)']*1.1
stdscens_pv_filled_dc['Utility-scale PV capacity (MWdc)'] = stdscens_added_cap_filled['Utility-scale PV capacity (MW)']*1.3
stdscens_pv_filled_dc
| Behind-the-meter PV capacity (MWdc) | Utility-scale PV capacity (MWdc) | |
|---|---|---|
| 2024 | NaN | NaN |
| 2025 | 7189.105 | 27019.395 |
| 2026 | 7189.105 | 27019.395 |
| 2027 | 9771.410 | 30790.955 |
| 2028 | 9771.410 | 30790.955 |
| 2029 | 13623.830 | 12296.830 |
| 2030 | 13623.830 | 12296.830 |
| 2031 | 10514.350 | 24948.040 |
| 2032 | 10514.350 | 24948.040 |
| 2033 | 7063.650 | 54768.545 |
| 2034 | 7063.650 | 54768.545 |
| 2035 | 2952.455 | 69713.410 |
| 2036 | 2952.455 | 69713.410 |
| 2037 | 2737.845 | 37732.045 |
| 2038 | 2737.845 | 37732.045 |
| 2039 | 2648.360 | 42390.270 |
| 2040 | 2648.360 | 42390.270 |
| 2041 | 2726.240 | 47168.615 |
| 2042 | 2726.240 | 47168.615 |
| 2043 | 3087.755 | 32563.895 |
| 2044 | 3087.755 | 32563.895 |
| 2045 | 3060.145 | 44384.470 |
| 2046 | 3060.145 | 44384.470 |
| 2047 | 3500.805 | 40158.755 |
| 2048 | 3500.805 | 40158.755 |
| 2049 | 3923.590 | 38699.570 |
| 2050 | 3923.590 | 38699.570 |
plt.plot(stdscens_pv_filled_dc)
plt.legend(stdscens_pv_filled_dc.columns)
plt.ylabel('Annual Installed Capacity\n[MWdc]')
Text(0, 0.5, 'Annual Installed Capacity\n[MWdc]')
#c-Si
MATERIALS = ['glass','aluminium_frames','silver','silicon', 'copper', 'encapsulant', 'backsheet']
moduleFile = os.path.join(baselinesfolder, 'baseline_modules_mass_US_updatedT50T90.csv')
#CdTe
MATERIALS_CdTe = ['glass_cdte','aluminium_frames_cdte', 'copper_cdte', 'encapsulant_cdte','cadmium','tellurium']
moduleFile_CdTe = os.path.join(baselinesfolder, 'baseline_modules_mass_US_CdTe.csv')
sim1 = PV_ICE.Simulation(name='MESC_StdScen', path=testfolder)
scens = ['23_MidCase_cSi', '23_MidCase_CdTe']
#c-Si
sim1.createScenario(name='23_MidCase_cSi', massmodulefile=moduleFile) #create the scenario, name and mod file attach
for mat in MATERIALS:
materialfile = os.path.join(baselinesfolder, 'baseline_material_mass_'+str(mat)+'.csv')
sim1.scenario['23_MidCase_cSi'].addMaterial(mat, massmatfile=materialfile) # add all materials listed in MATERIALS
#CdTe
sim1.createScenario(name='23_MidCase_CdTe', massmodulefile=moduleFile_CdTe) #create the scenario, name and mod file attach
for mat in MATERIALS_CdTe:
materialfile = os.path.join(baselinesfolder, 'baseline_material_mass_'+str(mat)+'.csv')
sim1.scenario['23_MidCase_CdTe'].addMaterial(mat, massmatfile=materialfile) # add all materials listed in MATERIALS_cdte
path = C:\Users\hmirletz\Documents\GitHub\PV_ICE\PV_ICE\TEMP\MESC-NRELStdScens Baseline folder directed to default: C:\Users\hmirletz\Documents\GitHub\PV_ICE\PV_ICE\baselines No energy module file passed. If desired, pass one of the following options: ['baseline_modules_energy.csv', 'baseline_modules_energy_CdTe.csv'] No energy module file passed. If desired, pass one of the following options: ['baseline_modules_energy.csv', 'baseline_modules_energy_CdTe.csv']
For future deployment in the US of c-Si and CdTe, the assumptions are:
We will linearly interpolate between historical CdTe Deployment and the 22 GW in 2030.
plt.plot(sim1.scenario['23_MidCase_CdTe'].dataIn_m.loc[:(2024-1995),['year','new_Installed_Capacity_[MW]']])
[<matplotlib.lines.Line2D at 0x26ff5ac85d0>, <matplotlib.lines.Line2D at 0x26ff5af3390>]
#linearly interpolate CdTe
#estimated 2024 install = 14GW
idx_temp = pd.RangeIndex(start=2024,stop=2051,step=1) #create the index
CdTeRamp = pd.DataFrame(index=idx_temp, columns=['CdTe_deploy_[MWdc]'], dtype=float)
CdTeRamp.loc[2024] = 14000
CdTeRamp.loc[2030] = 22000
CdTeRamp_full = round(CdTeRamp.interpolate(),0)
#CdTeRamp_full
#Modify the CdTe Scenario deployment schedule
sim1.modifyScenario(scenarios='23_MidCase_CdTe',stage='new_Installed_Capacity_[MW]',
value=CdTeRamp_full.sum(axis=1), start_year=2024) #
plt.plot(sim1.scenario['23_MidCase_CdTe'].dataIn_m.loc[:,'new_Installed_Capacity_[MW]'])
plt.ylabel('Annual Installed Capacity\n[MWdc]')
Text(0, 0.5, 'Annual Installed Capacity\n[MWdc]')
#now create the silicon deployment by subtracting CdTe from the total Utility deployment, add resi
mescdeploybytech = pd.DataFrame()
utilitydeploytotal = stdscens_pv_filled_dc['Utility-scale PV capacity (MWdc)'].values
CdTedeployutility = sim1.scenario['23_MidCase_CdTe'].dataIn_m.loc[(2024-1995):,'new_Installed_Capacity_[MW]'].values
resi = stdscens_pv_filled_dc['Behind-the-meter PV capacity (MWdc)'].values
mescdeploybytech['cSi_[MWdc]'] = utilitydeploytotal-CdTedeployutility+resi
mescdeploybytech.iloc[0,0]=14000 #fix nan issue
mescdeploybytech['CdTe_[MWdc]'] = sim1.scenario['23_MidCase_CdTe'].dataIn_m.loc[(2024-1995):,'new_Installed_Capacity_[MW]'].values
mescdeploybytech.index = idx_temp
plt.plot(mescdeploybytech)
plt.legend(mescdeploybytech.columns)
plt.ylabel('Annual Installed Capacity\n[MWdc]')
Text(0, 0.5, 'Annual Installed Capacity\n[MWdc]')
#Modify the c-Si deployment schedule
sim1.modifyScenario(scenarios=['23_MidCase_cSi'],stage='new_Installed_Capacity_[MW]',
value=mescdeploybytech['cSi_[MWdc]'], start_year=2024) #
yeargraph = sim1.scenario['23_MidCase_cSi'].dataIn_m.loc[:,'year']
plt.plot(yeargraph,sim1.scenario['23_MidCase_cSi'].dataIn_m.loc[:,'new_Installed_Capacity_[MW]'], label='cSi')
plt.plot(yeargraph,sim1.scenario['23_MidCase_CdTe'].dataIn_m.loc[:,'new_Installed_Capacity_[MW]'], label='CdTe')
plt.legend()
plt.ylim(0,)
plt.xlim(1995,2050)
plt.ylabel('Annual Installed Capacity\n[MWdc]')
Text(0, 0.5, 'Annual Installed Capacity\n[MWdc]')
Assume high recycling = Solar Cycle ideal (Dias 2022, Renewable and Sustainable Energy Reviews)
We don't need to create a high CdTe recycling scenario, already set to 100% collect and recycle.
sim1.createScenario(name='23_MidCase_cSi_hiR', massmodulefile=moduleFile)
for mat in MATERIALS:
materialfile = os.path.join(baselinesfolder, 'baseline_material_mass_'+str(mat)+'.csv')
sim1.scenario['23_MidCase_cSi_hiR'].addMaterial(mat, massmatfile=materialfile) # add all materials listed in MATERIALS
#modify deployment curve as before
sim1.modifyScenario(scenarios=['23_MidCase_cSi_hiR'],stage='new_Installed_Capacity_[MW]',
value=mescdeploybytech['cSi_[MWdc]'], start_year=2024) #
#modify EoL recycling variables
#module
sim1.modifyScenario(scenarios=['23_MidCase_cSi_hiR'],stage='mod_EOL_collection_eff', value=75, start_year=2024) #collect 75%
sim1.modifyScenario(scenarios=['23_MidCase_cSi_hiR'],stage='mod_EOL_pg4_recycled', value=100, start_year=2024) #recycle all collected
sim1.modifyScenario(scenarios=['23_MidCase_cSi_hiR'],stage='mod_EOL_pb4_recycled', value=100, start_year=2024) #
#material, all become a recycling target, and send to high quality
sim1.scenario['23_MidCase_cSi_hiR'].modifyMaterials('glass', 'mat_PG4_Recycling_target', 100, start_year=2024)
sim1.scenario['23_MidCase_cSi_hiR'].modifyMaterials('glass', 'mat_EOL_Recycled_into_HQ', 100, start_year=2024)
sim1.scenario['23_MidCase_cSi_hiR'].modifyMaterials('aluminium_frames', 'mat_PG4_Recycling_target', 100, start_year=2024)
sim1.scenario['23_MidCase_cSi_hiR'].modifyMaterials('aluminium_frames', 'mat_EOL_Recycled_into_HQ', 100, start_year=2024)
sim1.scenario['23_MidCase_cSi_hiR'].modifyMaterials('silver', 'mat_PG4_Recycling_target', 100, start_year=2024)
sim1.scenario['23_MidCase_cSi_hiR'].modifyMaterials('silver', 'mat_EOL_Recycled_into_HQ', 100, start_year=2024)
sim1.scenario['23_MidCase_cSi_hiR'].modifyMaterials('copper', 'mat_PG4_Recycling_target', 100, start_year=2024)
sim1.scenario['23_MidCase_cSi_hiR'].modifyMaterials('copper', 'mat_EOL_Recycled_into_HQ', 100, start_year=2024)
sim1.scenario['23_MidCase_cSi_hiR'].modifyMaterials('silicon', 'mat_PG4_Recycling_target', 100, start_year=2024)
sim1.scenario['23_MidCase_cSi_hiR'].modifyMaterials('silicon', 'mat_EOL_Recycled_into_HQ', 100, start_year=2024)
No energy module file passed. If desired, pass one of the following options: ['baseline_modules_energy.csv', 'baseline_modules_energy_CdTe.csv']
#sim1.scenario['23_MidCase_CdTe'].dataIn_m['mod_EOL_collection_eff']
#sim1.scenario['23_MidCase_cSi'].dataIn_m['mod_EOL_collection_eff']
#sim1.scenario['23_MidCase_cSi_hiR'].material['glass'].matdataIn_m.keys()
sim1.calculateMassFlow()
>>>> Calculating Material Flows <<<< Working on Scenario: 23_MidCase_cSi ******************** Finished Area+Power Generation Calculations ==> Working on Material : glass ==> Working on Material : aluminium_frames ==> Working on Material : silver ==> Working on Material : silicon ==> Working on Material : copper ==> Working on Material : encapsulant ==> Working on Material : backsheet Working on Scenario: 23_MidCase_CdTe ******************** Finished Area+Power Generation Calculations ==> Working on Material : glass_cdte ==> Working on Material : aluminium_frames_cdte Recycled surplus End of Sim for Mat aluminium_frames_cdte Scenario 23_MidCase_CdTe = 14469.188118420869 tonnes. ==> Working on Material : copper_cdte ==> Working on Material : encapsulant_cdte ==> Working on Material : cadmium ==> Working on Material : tellurium
C:\Users\hmirletz\Documents\GitHub\PV_ICE\PV_ICE\main.py:1427: RuntimeWarning: invalid value encountered in scalar subtract recycledsurplus = ( dm['mat_MFG_Recycled_HQ_into_MFG'].loc[rr] + #mfg scrap in that year
Working on Scenario: 23_MidCase_cSi_hiR ******************** Finished Area+Power Generation Calculations Warning: Paths 0 through 4 add to above 100%;Fixing by Updating Landfill value to the remainder of100-(P0+P2+P3+P4). Warning: Paths B 1 through 4 add to above 100%;Fixing by Updating Landfill value to the remainder of100-(P2+P3+P4). ==> Working on Material : glass ==> Working on Material : aluminium_frames ==> Working on Material : silver ==> Working on Material : silicon ==> Working on Material : copper ==> Working on Material : encapsulant ==> Working on Material : backsheet
ii_yearly, ii_cumu = sim1.aggregateResults()
sim1.plotMetricResults()
["MESC', 'StdScen', '23', 'MidCase', 'cSi", "MESC', 'StdScen', '23', 'MidCase', 'CdTe", "MESC', 'StdScen', '23', 'MidCase', 'cSi', 'hiR"]
recycled_by_mat = pd.DataFrame()
for mats in MATERIALS:
recycled_by_mat[str(mats)] = sim1.scenario['23_MidCase_cSi'].material[mats].matdataOut_m['mat_EOL_Recycled_2_HQ']
for mats in MATERIALS:
recycled_by_mat[str(mats+'_hiR')] = sim1.scenario['23_MidCase_cSi_hiR'].material[mats].matdataOut_m['mat_EOL_Recycled_2_HQ']
for mats_cdte in MATERIALS_CdTe:
recycled_by_mat[str(mats_cdte)] = sim1.scenario['23_MidCase_CdTe'].material[mats_cdte].matdataOut_m['mat_EOL_Recycled_2_HQ']
recycled_by_mat.index = pd.RangeIndex(start=1995,stop=2051,step=1)
recycled_by_mat_tonnes = recycled_by_mat/1000000 # This is the ratio for grams to Metric tonnes
#SUM and Annual 2024-2030,
recycled_by_mat_tonnes.loc[2024:2030,]
| glass | aluminium_frames | silver | silicon | copper | encapsulant | backsheet | glass_hiR | aluminium_frames_hiR | silver_hiR | silicon_hiR | copper_hiR | encapsulant_hiR | backsheet_hiR | glass_cdte | aluminium_frames_cdte | copper_cdte | encapsulant_cdte | cadmium | tellurium | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2024 | 19.170014 | 41.500447 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1775.001253 | 518.755591 | 18.456992 | 297.762340 | 2.830843 | 0.0 | 0.0 | 66.045262 | 0.004125 | 1.643801 | 0.0 | 0.186428 | 0.200481 |
| 2025 | 20.347898 | 41.391789 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1884.064635 | 517.397360 | 17.851945 | 262.735362 | 3.003694 | 0.0 | 0.0 | 101.931271 | 0.013053 | 2.537586 | 0.0 | 0.283476 | 0.304844 |
| 2026 | 22.019460 | 44.069153 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 2038.838870 | 550.864410 | 17.390710 | 232.389187 | 3.248966 | 0.0 | 0.0 | 153.969708 | 0.035230 | 3.834151 | 0.0 | 0.422172 | 0.453994 |
| 2027 | 7.799168 | 14.955748 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 722.145220 | 186.946851 | 3.192390 | 75.928343 | 1.145072 | 0.0 | 0.0 | 227.898120 | 0.084672 | 5.676870 | 0.0 | 0.616539 | 0.663012 |
| 2028 | 65.970255 | 130.824521 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 6108.356991 | 1635.306514 | 50.463798 | 669.531092 | 9.738106 | 0.0 | 0.0 | 330.863692 | 0.186636 | 8.244510 | 0.0 | 0.883813 | 0.950432 |
| 2029 | 67.169584 | 131.356918 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 6219.405893 | 1641.961472 | 45.872569 | 650.814490 | 9.910815 | 0.0 | 0.0 | 471.541081 | 0.385350 | 11.754295 | 0.0 | 1.244634 | 1.338451 |
| 2030 | 90.021086 | 174.120358 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 8335.285768 | 2176.504475 | 56.420174 | 872.170952 | 13.281751 | 0.0 | 0.0 | 660.172328 | 0.756794 | 16.463090 | 0.0 | 1.723065 | 1.852947 |
recycled_by_mat_tonnes.loc[2024:2030,'glass':'backsheet']
| glass | aluminium_frames | silver | silicon | copper | encapsulant | backsheet | |
|---|---|---|---|---|---|---|---|
| 2024 | 19.170014 | 41.500447 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 2025 | 20.347898 | 41.391789 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 2026 | 22.019460 | 44.069153 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 2027 | 7.799168 | 14.955748 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 2028 | 65.970255 | 130.824521 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 2029 | 67.169584 | 131.356918 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 2030 | 90.021086 | 174.120358 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
#set plot parameters
plt.rcParams.update({'font.size': 16})
plt.plot(recycled_by_mat_tonnes.loc[2024:2030,'glass':'backsheet'])
plt.ylim(0,10000)
plt.xlim(2024,2030)
plt.title('Annual End of Life Module Materials:\nc-Si Low Recycling')
plt.ylabel('EoL Module Materials\n[metric tonnes]')
plt.legend(recycled_by_mat_tonnes.columns, fontsize=14)
plt.grid()
plt.plot(recycled_by_mat_tonnes.loc[2024:2030,].filter(like='_hiR'))
plt.ylim(0,10000)
plt.xlim(2024,2030)
plt.title('Annual End of Life Module Materials:\nc-Si High Recycling')
plt.ylabel('EoL Module Materials\n[metric tonnes]')
plt.legend(recycled_by_mat_tonnes.columns, fontsize=14)
plt.grid()
plt.plot(recycled_by_mat_tonnes.loc[2024:2030,'glass_cdte':])
plt.ylim(0,2000)
plt.xlim(2024,2030)
plt.title('Annual End of Life Module Materials:\nCdTe')
plt.ylabel('EoL Module Materials\n[metric tonnes]')
plt.legend(recycled_by_mat_tonnes.loc[2024:2030,'glass_cdte':].columns, fontsize=14)
plt.grid()
recycled_by_mat_tonnes.loc[2024:2030,].sum(axis=0)
glass 292.497465 aluminium_frames 578.218934 silver 0.000000 silicon 0.000000 copper 0.000000 encapsulant 0.000000 backsheet 0.000000 glass_hiR 27083.098630 aluminium_frames_hiR 7227.736673 silver_hiR 209.648577 silicon_hiR 3061.331765 copper_hiR 43.159248 encapsulant_hiR 0.000000 backsheet_hiR 0.000000 glass_cdte 2012.421462 aluminium_frames_cdte 1.465860 copper_cdte 50.154305 encapsulant_cdte 0.000000 cadmium 5.360127 tellurium 5.764160 dtype: float64